﻿#include "stdafx.h"
#include "BlockAreaController.h"
#include <ctime>

inline CPoint BlockAreaController::TransformID( UINT uID ) const{
	uID = uID - BLOCK_ID_MIN + 1;
	return CPoint( (uID - 1) % m_BlockNumX + 1 , (uID - 1) / m_BlockNumX + 1 );// (i/px , i%px
}

inline UINT BlockAreaController::TransformID( CPoint *lpPoint )const{
	return (m_BlockNumX*(lpPoint->y-1) + lpPoint->x) - 1 + BLOCK_ID_MIN;//i=(y-1)*px+x
}

void BlockAreaController::ResizeMap( map<int, auto_ptr<CBlock> >::size_type NewSize)
{
	assert( NewSize < BLOCK_ID_MAX - BLOCK_ID_MIN );
	map<int, auto_ptr<CBlock> >::size_type OldSize = m_mBlockMap.size();
	for( ; OldSize > NewSize ; OldSize-- ){
		m_mBlockMap.erase( m_mBlockMap.end() );
	}
	for( ; OldSize < NewSize ; OldSize++ ){
		//CPoint pTmp = TransformID(BLOCK_ID_MIN + OldSize);
		CBlock *pBlock = new CBlock( m_pParent, BlockSize.cx, BlockSize.cy, BLOCK_ID_MIN + OldSize, 0 , 0 );
		m_mBlockMap[ BLOCK_ID_MIN + OldSize ].reset( pBlock );
	}
}

void BlockAreaController::RebuildAreaByMap( int BlockNum_X , int BlockNum_Y ){
	if( m_vBlockArea.size() == BlockNum_Y+1 || ( m_vBlockArea.size() && (m_vBlockArea.at(0).size() == BlockNum_X+1)) ){
		return;
	}
	m_vBlockArea.clear();
	
	m_vBlockArea.resize( BlockNum_Y + 1 );
	for(int i = 1 ; i != BlockNum_Y+1 ; i++ ){
		m_vBlockArea[i].resize( BlockNum_X + 1 );
	}
	CPoint pos ;
	for( map<int, auto_ptr<CBlock> >::iterator itr = m_mBlockMap.begin(); itr != m_mBlockMap.end() ; itr++ ){
		pos = TransformID(itr->second->GetThisBlockId());
		CBlock *pThis = itr->second.get();
		m_vBlockArea[pos.y][pos.x] = pThis;
		pThis->MakePosition( &CPoint(AreaPosition.x + (pos.x-1)*BlockSize.cx , AreaPosition.y + (pos.y-1)*BlockSize.cy) , &CSize(BlockSize.cx, BlockSize.cy) );
		//AreaPosition.x + pTmp.x*BlockSize.cx , AreaPosition.y + pTmp.y*BlockSize.cy 
		//BlockSize.cx, BlockSize.cy
	}
}

void BlockAreaController::ReInitialization(){
	map<int, auto_ptr<CBlock> >::iterator itr = m_mBlockMap.begin();
	for(; itr != m_mBlockMap.end() ; itr++ ){
		itr->second->MakeInitialization();
	}

	m_vRoundBlocks.clear();
	m_CurrentClickedBlockPos.SetPoint(0,0);
	m_nCurrentMineNum = m_nMineNum;
	m_bDidMakeMine = false;
	m_nLastRDownBlockuId = 0;
	m_nLastLDownBlockuId = 0;

	m_pParent->Invalidate();
}

void BlockAreaController::CreateBlocks( int nMineNum, int BlockNum_X, int BlockNum_Y, int BlockSize_X, int BlockSize_Y , int Area_X , int Area_Y )
{
	if( m_bDidCreateBlocks ){
		ReCreateBlocks(nMineNum, BlockNum_X, BlockNum_Y, BlockSize_X, BlockSize_Y , Area_X , Area_Y);
		return;
	}
	m_bDidCreateBlocks = true;
	m_nMineNum = nMineNum;
	m_BlockNumX = BlockNum_X;
	m_BlockNumY = BlockNum_Y;
	m_BlockNum = BlockNum_X * BlockNum_Y;
	AreaPosition.x = Area_X;
	AreaPosition.y = Area_Y;
	BlockSize.cx = BlockSize_X;
	BlockSize.cy = BlockSize_Y;

	ResizeMap( m_BlockNum );

	RebuildAreaByMap( m_BlockNumX , m_BlockNumY );

	ShowArea();
}

void BlockAreaController::ResetBlocks( int nMineNum ){
	assert( m_bDidCreateBlocks );
	if( nMineNum != -1 )
		m_nMineNum = nMineNum;

	ReInitialization();
}

void BlockAreaController::ReCreateBlocks( int& nMineNum, int& BlockNum_X, int& BlockNum_Y, int& BlockSize_X, int& BlockSize_Y , int& Area_X , int& Area_Y ){
	assert( m_bDidCreateBlocks );
	m_bDidCreateBlocks = true;
	m_nMineNum = nMineNum;
	m_BlockNumX = BlockNum_X;
	m_BlockNumY = BlockNum_Y;
	m_BlockNum = BlockNum_X * BlockNum_Y;
	AreaPosition.x = Area_X;
	AreaPosition.y = Area_Y;
	BlockSize.cx = BlockSize_X;
	BlockSize.cy = BlockSize_Y;

	ResizeMap( m_BlockNum );

	RebuildAreaByMap( m_BlockNumX , m_BlockNumY );

	ReInitialization();

	ShowArea();
}

void BlockAreaController::ClearBlocks()
{
	if (!m_bDidCreateBlocks)
		return;

	m_bDidMakeMine = false;
	m_bDidCreateBlocks = false;
	m_pParent = NULL;
	m_nMineNum = 0;
	m_BlockNumX = 0;
	m_BlockNumY = 0;
	m_BlockNum = 0;

	m_mBlockMap.clear();
	m_vBlockArea.clear();
}

void BlockAreaController::ShowArea()
{
	WindowUpdateLock l( m_pParent );
	map<int, auto_ptr<CBlock> >::iterator itr = m_mBlockMap.begin();
	for( ; itr != m_mBlockMap.end() ; itr++ )
	{
		itr->second->MakeVisable( true );
	}
}

void BlockAreaController::HideArea()
{
	WindowUpdateLock l( m_pParent );
	map<int, auto_ptr<CBlock> >::iterator itr = m_mBlockMap.begin();
	for( ; itr != m_mBlockMap.end() ; itr++ )
	{
		itr->second->MakeVisable( false );
	}
}

BlockAreaController::BlockAreaController()
	: m_bDidMakeMine(false), m_bDidCreateBlocks(false), m_nLastRDownBlockuId(0), m_nLastLDownBlockuId(0), m_pParent(NULL), AreaPosition(0,0), BlockSize(0,0)
{
}

BlockAreaController::BlockAreaController( CWnd* window )
	: m_bDidMakeMine(false), m_bDidCreateBlocks(false), m_nLastRDownBlockuId(0), m_nLastLDownBlockuId(0), m_pParent(window), AreaPosition(0,0), BlockSize(0,0)
{
}

BlockAreaController::~BlockAreaController()
{
	ClearBlocks();
}

void BlockAreaController::CheckMine(UINT uId)
{
	if (!m_bDidMakeMine)
		MakeMine(m_nMineNum, uId);

	CBlock* CurrentClickedBlock = FindBlock(uId);

	if ( CurrentClickedBlock->IsOpened() || CurrentClickedBlock->GetCareState() == CS_FLAG ) //打开则退出，若未打开但为旗子也退出
		return;

	CurrentClickedBlock->MakeOpened();
	if (CurrentClickedBlock->IsMine())
	{
		CurrentClickedBlock->SetImage(IDB_THEMINE);
		Event( AE_BOMB_BOOM );
	}
	else
	{
		GetBlocksAround(uId);
		int MineNum = GetAroundMineNum();
		switch (MineNum)
		{
		case 0:
			CurrentClickedBlock->SetImage(IDB_MINE_0);
			NonMine(uId);
			break;
		case 1:
			CurrentClickedBlock->SetImage(IDB_MINE_1);
			break;
		case 2:
			CurrentClickedBlock->SetImage(IDB_MINE_2);
			break;
		case 3:
			CurrentClickedBlock->SetImage(IDB_MINE_3);
			break;
		case 4:
			CurrentClickedBlock->SetImage(IDB_MINE_4);
			break;
		case 5:
			CurrentClickedBlock->SetImage(IDB_MINE_5);
			break;
		case 6:
			CurrentClickedBlock->SetImage(IDB_MINE_6);
			break;
		case 7:
			CurrentClickedBlock->SetImage(IDB_MINE_7);
			break;
		case 8:
			CurrentClickedBlock->SetImage(IDB_MINE_8);
			break;
		}
		Event( AE_UNCOVER_BLOCK );
	}
	CurrentClickedBlock->Invalidate();
	CurrentClickedBlock->UpdateWindow();
}

void BlockAreaController::GetBlocksAround( UINT uId )
{
	m_CurrentClickedBlockPos = TransformID(uId);
	m_vRoundBlocks.clear();

	if (m_CurrentClickedBlockPos.x != 1)
		m_vRoundBlocks.push_back(FindBlock(uId - 1));	//左
	else
		m_vRoundBlocks.push_back(NULL);

	if (m_CurrentClickedBlockPos.x != m_BlockNumX)
		m_vRoundBlocks.push_back(FindBlock(uId + 1));	//右
	else
		m_vRoundBlocks.push_back(NULL);

	if (m_CurrentClickedBlockPos.y != 1)
		m_vRoundBlocks.push_back(FindBlock(uId - m_BlockNumX));	//上
	else
		m_vRoundBlocks.push_back(NULL);

	if (m_CurrentClickedBlockPos.y != m_BlockNumY)
		m_vRoundBlocks.push_back(FindBlock(uId + m_BlockNumX)); //下
	else
		m_vRoundBlocks.push_back(NULL);

	if ((m_CurrentClickedBlockPos.y != 1) && (m_CurrentClickedBlockPos.x != 1))
		m_vRoundBlocks.push_back(FindBlock(uId - m_BlockNumX - 1));	//左上
	else
		m_vRoundBlocks.push_back(NULL);

	if ((m_CurrentClickedBlockPos.y != 1) && (m_CurrentClickedBlockPos.x != m_BlockNumX))
		m_vRoundBlocks.push_back(FindBlock(uId - m_BlockNumX + 1));	//右上
	else
		m_vRoundBlocks.push_back(NULL);

	if ((m_CurrentClickedBlockPos.y != m_BlockNumY) && (m_CurrentClickedBlockPos.x != 1))
		m_vRoundBlocks.push_back(FindBlock(uId + m_BlockNumX - 1)); //左下
	else
		m_vRoundBlocks.push_back(NULL);

	if ((m_CurrentClickedBlockPos.y != m_BlockNumY) && (m_CurrentClickedBlockPos.x != m_BlockNumX))
		m_vRoundBlocks.push_back(FindBlock(uId + m_BlockNumX + 1)); //右下
	else
		m_vRoundBlocks.push_back(NULL);
}

int BlockAreaController::GetAroundMineNum()
{
	int MineNum = 0;
	for (vector<CBlock*>::iterator it = m_vRoundBlocks.begin();it != m_vRoundBlocks.end();it++)
	{
		if (*it)
		{
			if ((*it)->IsMine())
				MineNum++;
		}

	}
	return MineNum;
}

void BlockAreaController::NonMine( UINT uId )
{
	GetBlocksAround(uId);
	vector<CBlock*> vRoundBlocks = m_vRoundBlocks;
	for (vector<CBlock*>::iterator it = vRoundBlocks.begin();it != vRoundBlocks.end();it++)
	{
		if (!*it)
			continue;
		if ((*it)->IsOpened())
			continue;
		CheckMine((*it)->GetThisBlockId());
	}
}

vector<CBlock*> BlockAreaController::GetUnOpenedAround( UINT uId )
{
	GetBlocksAround(uId);
	vector<CBlock*> vUnOpenedBlocks = m_vRoundBlocks;
	for (vector<CBlock*>::iterator it = vUnOpenedBlocks.begin();it != vUnOpenedBlocks.end();it++)
	{
		if (*it != NULL)
		{
			if ((*it)->IsOpened())
				*it = NULL;
		}
	}
	return vUnOpenedBlocks;
}

void BlockAreaController::MakeMine(int nMineNum, UINT uId)
{
	int nCurrentMineNum = 0;

	srand(time(NULL));
	while(nCurrentMineNum < nMineNum)
	{
		//CBlock* Block = m_vBlockArea.at( rand() % (m_BlockNumY - 1) + 1 ).at(rand() % (m_BlockNumX - 1) + 1);
		CBlock* Block = m_mBlockMap.at( RAND( BLOCK_ID_MIN , BLOCK_ID_MIN + m_BlockNum ) ).get();
		if ((!Block->IsMine()) && ((Block->GetThisBlockId()) != uId))
		{
			Block->MakeMine();
			nCurrentMineNum++;
		}
	}
	m_bDidMakeMine = true;
}

void BlockAreaController::CheckedBlock( UINT uId )
{
	if (m_nLastRDownBlockuId != uId)
	{
		CBlock* LastClickedBlock = FindBlock(m_nLastRDownBlockuId);
		LastClickedBlock->MakeRPressFalse();
		LRReleasUp(m_nLastRDownBlockuId);
		return;
	}

	CBlock* CurrentClickedBlock = FindBlock(uId);
	if (CurrentClickedBlock->IsOpened())
		return;

	int care = CurrentClickedBlock->GetCareState();//现在的图标
	CurrentClickedBlock->MakeCareState( ++care );//切换到下一种

	switch ( care )
	{
	case CS_FLAG://空--》旗子
		CurrentClickedBlock->SetImage(IDB_FLAG);
		break;
	case CS_BOMB://旗子--》预测炸弹
		CurrentClickedBlock->SetImage(IDB_BOMB);
		break;
	case CS_OVER://预测炸弹--》空
	default:
		CurrentClickedBlock->MakeCareState( CS_NONE );
		CurrentClickedBlock->SetImage(IDB_NORMAL);
		break;
	}
	CurrentClickedBlock->Invalidate();
	CurrentClickedBlock->UpdateWindow();
}

void BlockAreaController::LRButtonCheackMine( UINT uId )
{
	CBlock* CurrentClickedBlock = FindBlock(uId);

	if (!CurrentClickedBlock->IsOpened())
	{
		LRReleasUp(uId);
		return;
	}

	GetBlocksAround(uId);
	int nFlagNum = GetAroundFlagNum();
	int nMineNum = GetAroundMineNum();
	if (nFlagNum == nMineNum)
	{
		vector<CBlock*> vRoundBlocks = m_vRoundBlocks;
		for(vector<CBlock*>::iterator it = vRoundBlocks.begin();it != vRoundBlocks.end();it++)
		{
			if (*it)
			{
				if ((*it)->GetCareState() != CS_FLAG)
				{
					(*it)->MakeCareState(0);
					CheckMine((*it)->GetThisBlockId());
				}
			}
		}
	}
	else
		LRReleasUp(uId);
}

int BlockAreaController::GetAroundFlagNum()
{
	int FlagNum = 0;
	for (vector<CBlock*>::iterator it = m_vRoundBlocks.begin();it != m_vRoundBlocks.end();it++)
	{
		if (*it)
		{
			if ( (*it)->GetCareState() == CS_FLAG )
				FlagNum++;
		}

	}
	return FlagNum;
}

void BlockAreaController::PressDown( UINT uId )
{
	CBlock* CurrentClickedBlock = FindBlock(uId);
	if (CurrentClickedBlock->IsOpened())
		return;

	switch (CurrentClickedBlock->GetCareState())
	{
	case CS_FLAG:
		return;
		break;
	case CS_BOMB:
		CurrentClickedBlock->SetImage(IDB_BOMBPRESS);
		break;
	case CS_NONE:
		CurrentClickedBlock->SetImage(IDB_MINE_0);
		break;
	}
	CurrentClickedBlock->Invalidate();
	CurrentClickedBlock->UpdateWindow();
}

void BlockAreaController::CheckBlockRDown( UINT uId )
{
	if (m_nLastRDownBlockuId == 0 )
	{
		m_nLastRDownBlockuId = uId;
		return;
	}
	CBlock* LastClickedBlock = FindBlock(m_nLastRDownBlockuId);
	LastClickedBlock->MakeRPressFalse();
	m_nLastRDownBlockuId = uId;
}

void BlockAreaController::ReleasUp( UINT uId )
{
	CBlock* CurrentClickedBlock = FindBlock(uId);
	if (CurrentClickedBlock->IsOpened())
		return;

	switch (CurrentClickedBlock->GetCareState())
	{
	case CS_FLAG:
		return;
		break;
	case CS_BOMB:
		CurrentClickedBlock->SetImage(IDB_BOMB);
		break;
	case CS_NONE:
		CurrentClickedBlock->SetImage(IDB_NORMAL);
		break;
	}
	CurrentClickedBlock->Invalidate();
	CurrentClickedBlock->UpdateWindow();
}

void BlockAreaController::LRPressDown( UINT uId )
{
	CBlock* CurrentClickedBlock = FindBlock(uId);
	vector<CBlock*> vUnOpenedBlocks = GetUnOpenedAround(uId);
	if (!CurrentClickedBlock->IsOpened())
		vUnOpenedBlocks.push_back(CurrentClickedBlock);
	for(vector<CBlock*>::iterator it = vUnOpenedBlocks.begin();it != vUnOpenedBlocks.end();it++)
	{
		if (*it)
		{
			PressDown((*it)->GetThisBlockId());
		}
	}
}

void BlockAreaController::LRReleasUp( UINT uId )
{
	CBlock* CurrentClickedBlock = FindBlock(uId);
	vector<CBlock*> vUnOpenedBlocks = GetUnOpenedAround(uId);
	if (!CurrentClickedBlock->IsOpened())
		vUnOpenedBlocks.push_back(CurrentClickedBlock);
	for(vector<CBlock*>::iterator it = vUnOpenedBlocks.begin();it != vUnOpenedBlocks.end();it++)
	{
		if (*it)
		{
			ReleasUp((*it)->GetThisBlockId());
		}
	}
}

void BlockAreaController::CheckBlockLDown( UINT uId )
{
	if (m_nLastLDownBlockuId == 0 )
	{
		m_nLastLDownBlockuId = uId;
		return;
	}
	CBlock* LastClickedBlock = FindBlock(m_nLastLDownBlockuId);
	LastClickedBlock->MakeLPressFalse();
	LRReleasUp(m_nLastLDownBlockuId);
	m_nLastLDownBlockuId = uId;
}

void BlockAreaController::Event(AreaEventId lAEID)
{
	m_aeLastEvent = lAEID;
}

const AreaEventId& BlockAreaController::GetLastEvent()const{
	return m_aeLastEvent;
}

void BlockAreaController::ClearEvent(){
	Event(AE_NOTHING);
}
